package com.kk.demo.unsface.obj;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;

import com.liujun.test.ObjectSizeFetcher;

import sun.misc.Unsafe;

/**
 * 使用java的Unsafe操作java对象
* 源文件名：UnsafeObjectProc.java
* 文件版本：1.0.0
* 创建作者：liujun
* 创建日期：2016年11月18日
* 修改作者：liujun
* 修改日期：2016年11月18日
* 文件描述：TODO
* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.
*/
public class UnsafeObjectProc {

    /**
     * 获取Unsafe对象
    * 方法描述
    * @return
    * @创建日期 2016年11月18日
    */
    @SuppressWarnings("restriction")
    public Unsafe getUnsafe() {
        Unsafe safe = null;

        Field fied = null;
        try {
            fied = Unsafe.class.getDeclaredField("theUnsafe");
            fied.setAccessible(true);
            safe = (Unsafe) fied.get(null);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return safe;
    }

    public static void main(String[] args) {
        UnsafeObjectProc unsafeObject = new UnsafeObjectProc();
        Unsafe unsafe = unsafeObject.getUnsafe();

        // 获取基本信息
        // unsafeObject.userBase(unsafe);

        // 通过unsafe设置属性的值
        // unsafeObject.procSetValue(unsafe);

        // 通过Unsafe进行数组的偏移量获取
        // unsafeObject.arrayListSetValue(unsafe);

        // 数组长度的输出
        // unsafeObject.arrayScaleOut(unsafe);
        // unsafeObject.towArraySetValue(unsafe);
        // 字节头信息
        // unsafeObject.getClassObject(unsafe);

        // 作业题1，
        // unsafeObject.procGetOffset(unsafe);

        // 作业题2
        // unsafeObject.arrayListgetValue(unsafe);
        // 设置第一个元素
        // unsafeObject.arrayListSetValue(unsafe);

        // 测试说明8的倍数
        unsafeObject.getObject32ArraySet(unsafe);
        unsafeObject.getObject64ArraySet(unsafe);
    }

    /**
     * 最基本的一个对比信息
    * 方法描述
    * @param unsafe
    * @创建日期 2016年11月18日
    */
    public void userBase(Unsafe unsafe) {
        System.out.println("当前的page size,即内存大小块:" + unsafe.pageSize());
        System.out.println("当前的操作jdk版本为:" + unsafe.addressSize());
        // 在64位jdk的情况下:当前的page size,即内存大小块:4096
        // 在64位jdk的情况下:当前的操作jdk版本为:8 代表64位
        // 在32位jdk的情况下:当前的page size,即内存大小块:4096
        // 在32位jdk的情况下:当前的操作jdk版本为:4 代表是32位
    }

    /**
     * 设置属性的值通过unsafe
    * 方法描述
    * @param unsafe
    * @创建日期 2016年11月18日
    */
    public void procSetValue(Unsafe unsafe) {
        UnSafeMyObject unsafeBean = new UnSafeMyObject();
        try {
            // objectFieldOffset 获取对象中的属性a在javabean中的偏移量
            long objOffset = unsafe.objectFieldOffset(
                    unsafeBean.getClass().getDeclaredField("a"));
            System.out.println(unsafeBean);
            // 通过偏移量来设置属性的值
            unsafe.putInt(unsafeBean, objOffset, 10);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }

        System.out.println(unsafeBean);
    }

    /**
     * 设置属性的值通过unsafe
     * 方法描述
     * @param unsafe
     * @创建日期 2016年11月18日
     */
    public void procGetOffset(Unsafe unsafe) {
        UnsafeMyObj01 unsafeBean = new UnsafeMyObj01();

        try {

            // objectFieldOffset 获取对象中的属性a在javabean中的偏移量
            long objOffset = unsafe.objectFieldOffset(
                    unsafeBean.getClass().getDeclaredField("c"));
            System.out.println("内存C的偏移地址:" + objOffset);
            getObjectSize(unsafeBean);
            // 通过偏移量来设置属性的值
            unsafe.putInt(unsafeBean, objOffset, 10);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }

        System.out.println(unsafeBean);
    }

    public void getObjectSize(Object read) {

        long value = ObjectSizeFetcher.getObjectSize(read);
        System.out.println("当前的大小为:" + value);
    }

    /**
     * 进行ArrayList的动态设置
     * 方法描述
     * @param unsafe
     * @创建日期 2016年11月18日
     */
    public void arrayListgetValue(Unsafe unsafe) {

        // 使用参数-XX:-UseCompressedOops 关闭指针压缩
        // 使用参数-XX:+UseCompressedOops 开启指针压缩
        System.out.println(
                "Arraylist数组的基本偏移量:" + unsafe.arrayBaseOffset(int[].class));
        System.out.println("Arraylist数组的元素1偏移量:"
                + unsafe.arrayIndexScale(int[].class) * 1);

        // 在32位条件下:Arraylist数组的基本偏移量:12
        // 在32位条件下:Arraylist数组的元素1偏移量:4
        // 在64位开启压缩的条件下:Arraylist数组的基本偏移量:16
        // 在64位开启压缩的条件下: Arraylist数组的元素1偏移量:4
        // 在64位关闭压缩的条件下:Arraylist数组的基本偏移量:24
        // 在64位关闭压缩的条件下:Arraylist数组的元素1偏移量:8
    }

    /**
     * 进行ArrayList的动态设置
    * 方法描述
    * @param unsafe
    * @创建日期 2016年11月18日
    */
    @SuppressWarnings("rawtypes")
    public void arrayListSetValue(Unsafe unsafe) {
        ArrayList[] arrList = new ArrayList[2];

        System.out.println(
                "Arraylist数组的偏移量:" + unsafe.arrayBaseOffset(ArrayList[].class));
        // 在jdk为32位下:Arraylist数组的中元素的长度为4
        // Array为4个字节的长度 + Array数组第几个元素，因为数组是固定元素长度
        int value = unsafe.arrayBaseOffset(ArrayList[].class);
        // + unsafe.arrayIndexScale(ArrayList[].class) * 0;

        System.out.println(Arrays.toString(arrList));

        ArrayList<Integer> arrayValuess = new ArrayList<>();
        arrayValuess.add(12);
        arrayValuess.add(13);

        unsafe.putObject(arrList, value, arrayValuess);
        System.out.println(Arrays.toString(arrList));
    }

    /**
     * 进行二维数组的
     * 方法描述
     * @param unsafe
     * @创建日期 2016年11月18日
     */
    @SuppressWarnings("rawtypes")
    public void towArraySetValue(Unsafe unsafe) {
        int[][] twoArray = new int[3][3];
        twoArray[0][0] = 129;
        twoArray[1][1] = 127;

        System.out.println(
                "int[][]数组中的基本的偏移量:" + unsafe.arrayBaseOffset(int[][].class));
        System.out.println(
                "int[]数组中的基本偏移量:" + unsafe.arrayBaseOffset(int[].class));
        System.out.println(
                "int[][]数组的偏移量:" + unsafe.arrayIndexScale(int[][].class));
        System.out
                .println("int[]数组的偏移量:" + unsafe.arrayIndexScale(int[].class));

        int x = 2;
        int y = 1;

        // 获取二维数组中的引用信息
        Object value = unsafe.getObject(twoArray,
                unsafe.arrayBaseOffset(int[][].class)
                        + unsafe.arrayIndexScale(int[][].class) * x);
        System.out.println(value);

        // 获取数组中元素信息
        int value2 = unsafe.getInt(value, unsafe.arrayBaseOffset(int[].class)
                + unsafe.arrayIndexScale(int[].class) * 1);
        System.out.println(value2);

        // 得到引用数组中的的偏移量
        long size = unsafe.arrayBaseOffset(int[].class)
                + unsafe.arrayIndexScale(int[].class) * y;

        // 设置对象中的值
        unsafe.putInt(value, size, 1209);

        System.out.println("最终二维的值:" + twoArray[x][y]);

        int i = 0;
        int j = 1;
        twoArray[i][j] = 2456;

        for (int s = 0; s < twoArray.length; s++) {
            for (int k = 0; k < twoArray[i].length; k++) {
                System.out.println(
                        "twoArray[" + s + "][" + k + "]的值 :" + twoArray[s][k]);
            }
        }
    }

    /**
     * 计算内存跨度
    * 方法描述
    * @param unsafe
    * @创建日期 2016年11月20日
    */
    public void countMoneySize(Unsafe unsafe) {
        int[][] res = new int[2048][2048];
        int[][] mul1 = new int[2048][2048];
        int[][] mul2 = new int[2048][2048];

        for (int i = 0; i < 2048; i++) {
            for (int j = 0; j < 2048; j++) {
                for (int k = 0; k < 2048; k++) {
                    res[i][j] += mul1[i][k] * mul2[k][j];
                }
            }
        }
    }

    /**
     * 进行输出数据中偏移长度的信息
    * 方法描述
    * @param unsafe
    * @创建日期 2016年11月18日
    */
    public void arrayScaleOut(Unsafe unsafe) {
        System.out.println("array scale , ArrayList 长度 ：4："
                + unsafe.arrayIndexScale(ArrayList[].class));
        System.out.println("array scale , UnSafeMyObject 长度 ：4："
                + unsafe.arrayIndexScale(UnSafeMyObject[].class));
        System.out.println("array scale , Long 长度 ：8："
                + unsafe.arrayIndexScale(long[].class));
        System.out.println("array scale , boolean 长度 ：1："
                + unsafe.arrayIndexScale(boolean[].class));
        System.out.println("array scale , Boolean 长度 ：4："
                + unsafe.arrayIndexScale(Boolean[].class));
        System.out.println("array scale , int 长度 ：4："
                + unsafe.arrayIndexScale(int[].class));
        System.out.println("array scale , Integer 长度 ：4："
                + unsafe.arrayIndexScale(Integer[].class));
        System.out.println("array scale , byte 长度 ：1："
                + unsafe.arrayIndexScale(byte[].class));

        System.out.println("数组的基本长度");
        System.out.println("array scale , ArrayList 基本 ：12："
                + unsafe.arrayBaseOffset(ArrayList[].class));
        System.out.println("array scale , UnSafeMyObject 长度 ：12："
                + unsafe.arrayBaseOffset(UnSafeMyObject[].class));
        System.out.println("array scale , Long 长度 ：16："
                + unsafe.arrayBaseOffset(long[].class));
        System.out.println("array scale , boolean 长度 ：12："
                + unsafe.arrayBaseOffset(boolean[].class));
        System.out.println("array scale , Boolean 长度 ：12："
                + unsafe.arrayBaseOffset(Boolean[].class));
        System.out.println("array scale , int 长度 ：12："
                + unsafe.arrayBaseOffset(int[].class));
        System.out.println("array scale , Integer 长度 ：12："
                + unsafe.arrayBaseOffset(Integer[].class));
        System.out.println("array scale , byte 长度 ：12："
                + unsafe.arrayBaseOffset(byte[].class));
    }

    /**
     * 获取class对象的对象信息
    * 方法描述
    * @param unsafe
    * @创建日期 2016年11月18日
    */
    public void getClassObject(Unsafe unsafe) {
        UnsafeObjectProc object = new UnsafeObjectProc();
        int versionType = unsafe.addressSize();
        // 如果为32位
        if (versionType == 4) {
            int addres = unsafe.getInt(object, 4l);
            System.out.println("当前的32字节码地址:" + addres);
            // 当前的32字节码地址:346230400
        }
        // 如果为64位
        if (versionType == 8) {
            // 使用参数-XX:-UseCompressedOops 关闭指针压缩
            // 使用参数-XX:+UseCompressedOops 开启指针压缩
            long addres = unsafe.getLong(object, 8l);
            System.out.println("当前的64字节码地址:" + addres);

            long addres2 = unsafe.getInt(object, 8l);
            System.out.println("当前的64字节码地址:" + addres2);

            long add = unsafe.getInt(object, 8L) & 0xffffffffL;
            System.out.println("当前的64字节码地址:" + add);

            // -XX:+UseCompressedOops
            // 当前的64字节码地址:4160798725
            // 当前的64字节码地址:-134168571
            // -XX:-UseCompressedOops
            // 当前的64字节码地址:464000192
            // 当前的64字节码地址:464000192
        }
    }

    /**
     * 针对32位jdk
    * 方法描述
    * @param unsafe
    * @创建日期 2016年11月20日
    */
    public void getObject32ArraySet(Unsafe unsafe) {
        Object helperArray[] = new Object[1];
        helperArray[0] = 1;

        System.out.println("在32位下");

        long baseOffset = unsafe.arrayBaseOffset(Object[].class);

        System.out.println("偏移量为:" + baseOffset);
        System.out.println("偏移量是否为8的倍数:" + baseOffset % 8);

        System.out.println("二进制信息偏移为:" + Long.toBinaryString(baseOffset));

        int addressOfObject = unsafe.getInt(helperArray, baseOffset);

        Object getValue = unsafe.getObject(helperArray, baseOffset);

        System.out.println("获取到的值:" + getValue);

        System.out.println("对象的偏移量为:" + addressOfObject);
        System.out.println("对象的检查是否为8的倍数:" + addressOfObject % 8);
    }

    /**
     * 针对64位jdk
     * 方法描述
     * @param unsafe
     * @创建日期 2016年11月20日
     */
    public void getObject64ArraySet(Unsafe unsafe) {
        System.out.println();
        System.out.println("在进行64位下");

        // 使用参数-XX:-UseCompressedOops 关闭指针压缩
        // 使用参数-XX:+UseCompressedOops 开启指针压缩

        Object helperArray[] = new Object[1];
        helperArray[0] = 2l;
        long baseOffset = unsafe.arrayBaseOffset(Object[].class);
        System.out.println("偏移量为:" + baseOffset);
        long addressOfObject = unsafe.getLong(helperArray, baseOffset);
        System.out.println("对象的地址为:" + addressOfObject);
        System.out.println("二进制信息地址为:" + Long.toBinaryString(addressOfObject));
        System.out.println("对象的检查是否为8的倍数:" + addressOfObject % 8);
    }
}
